﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using app.Models;
using AmuTools;
using System.IO;
using System.Security.Cryptography;
using System.Web.Security;

namespace app.Api
{
    /*
        上传

        添加了百度富文本编辑器ueditor。引入ueditor流程如下：
        前端：引入脚本文件，配置接口信息。注意前端加载时会异步加载脚本和样式等信息，
            由于没有参与打包，所以要放在Common文件夹中。也就是说前端代码被拆分成了两部分，
            参与打包的文件放在views中，不参与打包的要放在app下的Common中。
        后端：引入工具类，配置信息。其中对工具类保存文件和读取文件过程做了修改
    */
    public class ApiUpload : ApiBase
    {
        public ApiUpload(Controller ctx) : base(ctx) { }

        private string root_path = "/Resources/";

        #region 自定义请求参数
        // 作者
        string req_author { get { return GetStringParam(new ReqStringParamOption { Name = "author", Level = REQPARAMLEVEL.FREE }); } }

        #endregion

        public void Do(string action)
        {
            switch (action)
            {
                case "get":
                    {
                        UploadedFile uf = G.DB.GetById<UploadedFile>(req_id);
                        ResponseJson(new ObjectResult { data = uf });
                        return;
                    }
            }


            if (UserIdentity != IDENTITY.ADMIN && UserIdentity != IDENTITY.MEMBER) ThrowStatus(STATUS.NO_PERMISSION);
            switch (action)
            {
                case "file":
                    List<UploadedFile> ulfs = UploadFile(UPLOAD_TYPE.File);
                    ResponseJson(new ListResult<UploadedFile> { list = ulfs });
                    break;
                case "delete": {
                        UploadedFile ulf = G.DB.GetById<UploadedFile>(req_id);
                        if (ulf != null)
                        {
                            G.DB.Delete<UploadedFile>(req_id);
                            DeleteFile(ulf.url);
                        }
                        ResponseJson(new Result());
                    } break;
                case "ueditor":
                    {
                        ProcessRequest(ctx.HttpContext);
                        break;
                    }
                default: break;
            }
        }
        

        public static string[] image_format = { ".jpg", ".gif", ".bmp", ".png" };
        public static string[] video_format = { ".mp4", ".ogg", ".wmv", ".flv" };
        public static string[] document_format = { ".txt", ".doc", ".docx" };
        public static string[] audio_format = { ".mp3" };
        public static string[] file_format = { ".jpg", ".gif", ".bmp", ".png", ".mp4", ".ogg", ".txt", ".doc", ".docx", ".mp3", ".zip", ".wmv", ".flv", ".exe", ".rar" };
        
        public List<UploadedFile> UploadFile(UPLOAD_TYPE ut = UPLOAD_TYPE.All)
        {
            List<UploadedFile> upload_list = new List<UploadedFile>();
            HttpFileCollectionBase files = ctx.Request.Files;
            int file_count = files.Count;
            if (file_count == 0) return upload_list;
            
            for (int i = 0; i < file_count; i++)
            {
                upload_list.Add(SaveFile(files[i], ut));
            }

            return upload_list;
        }

        public UploadedFile SaveFile(HttpPostedFileBase hpf, UPLOAD_TYPE ut = UPLOAD_TYPE.All)
        {
            byte[] uploadFileBytes = new byte[hpf.ContentLength];
            hpf.InputStream.Read(uploadFileBytes, 0, hpf.ContentLength);
            return SaveFile(uploadFileBytes, hpf.FileName, ut);
        }

        public UploadedFile SaveFile(byte[] uploadFileBytes, string file_name, UPLOAD_TYPE ut = UPLOAD_TYPE.All)
        {
            string file_extension = Path.GetExtension(file_name).ToLower(); // 得到扩展名
            if (!IsValidFormat(file_extension, ut)) return null; // 检测是不是合法类型

            string new_path = GetUploadPath() + CreatePasswordHash(file_name, 4) + file_extension; // 生成新的路径

            // 先存到数据库
            UploadedFile ulf = new UploadedFile { url = new_path, create_time = DateTimeEx.GetNowTimeStamp(), size = uploadFileBytes.Length, name = file_name, person_id = UserCode };
            G.DB.Insert<UploadedFile>(ulf);
            // 再存到本地
            string full_new_path = AppDomain.CurrentDomain.BaseDirectory + new_path;//转换成服务器上的物理路径，用来保存
            // 保存图片
            File.WriteAllBytes(full_new_path, uploadFileBytes);

            return ulf;
        }

        public void DeleteFile(string path)
        {
            string full_path = AppDomain.CurrentDomain.BaseDirectory + path;
            if (File.Exists(full_path))
            {
                File.Delete(full_path);
            }
        }

        /// <summary>
        /// 验证是否指定的图片格式
        /// </summary>
        /// <returns></returns>
        private static bool IsValidFormat(string str, UPLOAD_TYPE upload_type)
        {
            if (upload_type == UPLOAD_TYPE.All) return true;

            string[] format_arr = new string[0];
            switch (upload_type)
            {
                case UPLOAD_TYPE.Image: format_arr = image_format; break;
                case UPLOAD_TYPE.Video: format_arr = video_format; break;
                case UPLOAD_TYPE.Doucment: format_arr = document_format; break;
                case UPLOAD_TYPE.Audio: format_arr = audio_format; break;
                case UPLOAD_TYPE.File: format_arr = file_format; break;
            }

            string thestr = str.ToLower();
            for (int i = 0; i < format_arr.Length; i++)
            {
                if (thestr == format_arr[i]) return true;
            }
            return false;
        }

        private string GetUploadPath()
        {
            DateTime now = DateTime.Now;
            string time_path = now.Year.ToString() + "/" + now.Month.ToString() + "-" + now.Day.ToString() + "/";

            string con_path = root_path + time_path;
            string full_con_path = AppDomain.CurrentDomain.BaseDirectory + con_path;

            if (!Directory.Exists(full_con_path))
            {
                Directory.CreateDirectory(full_con_path);
            }

            return con_path;
        }

        /// <summary>
        /// 创建一个指定长度的随机salt值
        /// </summary>
        private static string CreateSalt(int saltLenght)
        {
            //生成一个加密的随机数
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] buff = new byte[saltLenght];
            rng.GetBytes(buff);
            //返回一个Base64随机数的字符串
            return Convert.ToBase64String(buff);
        }

        /// <summary>
        /// 返回加密后的字符串
        /// </summary>
        private static string CreatePasswordHash(string pwd, int saltLenght)
        {
            string strSalt = CreateSalt(saltLenght);
            //把密码和Salt连起来
            string saltAndPwd = String.Concat(pwd, strSalt);
            //对密码进行哈希
            string hashenPwd = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPwd, "sha1");
            //转为小写字符并截取前16个字符串
            hashenPwd = hashenPwd.ToLower().Substring(0, 16);
            //返回哈希后的值
            return hashenPwd;
        }

        public void ProcessRequest(HttpContextBase context)
        {
            Handler action = null;
            switch (context.Request["action"])
            {
                case "config":
                    action = new ConfigHandler(context);
                    break;
                case "uploadimage":
                    action = new UploadHandler(ctx, new UploadConfig()
                    {
                        AllowExtensions = Config.GetStringList("imageAllowFiles"),
                        PathFormat = Config.GetString("imagePathFormat"),
                        SizeLimit = Config.GetInt("imageMaxSize"),
                        UploadFieldName = Config.GetString("imageFieldName")
                    });
                    break;
                case "uploadscrawl":
                    action = new UploadHandler(ctx, new UploadConfig()
                    {
                        AllowExtensions = new string[] { ".png" },
                        PathFormat = Config.GetString("scrawlPathFormat"),
                        SizeLimit = Config.GetInt("scrawlMaxSize"),
                        UploadFieldName = Config.GetString("scrawlFieldName"),
                        Base64 = true,
                        Base64Filename = "scrawl.png"
                    });
                    break;
                case "uploadvideo":
                    action = new UploadHandler(ctx, new UploadConfig()
                    {
                        AllowExtensions = Config.GetStringList("videoAllowFiles"),
                        PathFormat = Config.GetString("videoPathFormat"),
                        SizeLimit = Config.GetInt("videoMaxSize"),
                        UploadFieldName = Config.GetString("videoFieldName")
                    });
                    break;
                case "uploadfile":
                    action = new UploadHandler(ctx, new UploadConfig()
                    {
                        AllowExtensions = Config.GetStringList("fileAllowFiles"),
                        PathFormat = Config.GetString("filePathFormat"),
                        SizeLimit = Config.GetInt("fileMaxSize"),
                        UploadFieldName = Config.GetString("fileFieldName")
                    });
                    break;
                case "listimage":
                    action = new ListFileManager(context, Config.GetString("imageManagerListPath"), Config.GetStringList("imageManagerAllowFiles"));
                    break;
                case "listfile":
                    action = new ListFileManager(context, Config.GetString("fileManagerListPath"), Config.GetStringList("fileManagerAllowFiles"));
                    break;
                case "catchimage":
                    action = new CrawlerHandler(context);
                    break;
                default:
                    action = new NotSupportedHandler(context);
                    break;
            }
            action.Process();
        }
    }

    public enum UPLOAD_TYPE
    {
        All = 0, // 所有
        Image = 1, // 图片
        Video = 2, // 视频
        Doucment = 3, // 文档
        Audio = 4, // 音频
        File = 5
    }

}